#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include "cond.hpp"
#include "Lock.hpp"
// 单生产单消费
// 一把锁，两个条件变量
template <class T>
class BlockQuenue
{
public:
    BlockQuenue(int n) : cap(n), product_wait(0), consum_wait(0)
    {
    }
    void Enqueue(const T &data)
    {
        mutex.lock();
        while (isfull())
        {
            product_wait++;
            product_cond.Wait(mutex);
            product_wait--;
        }
        blockqueue.push(data);
        if (consum_wait > 0)
        {
            // 唤醒消费者
            consum_cond.Notify();
        }
        mutex.unlock();
    }
    // 消费者拿数据
    void Popenque(T *out)
    {
        mutex.lock();
        if (isempty())
        {
            consum_wait++;
            consum_cond.Wait(mutex);
            consum_wait--;
        }
        // 被唤醒后进行消费
        *out = blockqueue.front();
        blockqueue.pop();
        if (product_wait > 0)
        {
            // 唤醒生产者
            product_cond.Notify();
        }
        mutex.unlock();
    }
    bool isfull()
    {
        return blockqueue.size() == cap;
    }
    bool isempty()
    {
        return blockqueue.empty();
    }
    ~BlockQuenue()
    {
    }
private:
    std::queue<T> blockqueue;
    int cap;

    Mutex mutex;

    Cond product_cond;
    Cond consum_cond;

    int product_wait;
    int consum_wait;
};